package com.hm.service.impl.wx;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.hm.service.framework.base.BaseServiceImpl;
import com.hm.service.service.wx.WeChatPublicService;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

@Service
public class WeChatPublicServiceImpl extends BaseServiceImpl implements WeChatPublicService {
    private static final Logger logger = LoggerFactory.getLogger(WeChatPublicServiceImpl.class);

    //微信公众号配置信息
    private static final String appID = "wx5ac3011b0709cb39";
    private static final String appsecret = "a695622e1874859729af63cbc88bdfbc";

    @Qualifier("OkHttpClient")
    public OkHttpClient okHttpClient;

    @Qualifier("JedisPool")
    private final JedisPool jedisPool;

    public WeChatPublicServiceImpl(OkHttpClient okHttpClient, JedisPool jedisPool) {
        this.okHttpClient = okHttpClient;
        this.jedisPool = jedisPool;
    }

    /**
     * 本步骤中获取到网页授权access_token的同时，也获取到了openid，snsapi_base式的网页授权流程即到此为止
     * 注意：同一个code只能请求一次资源，否则会报错:
     * {"errcode":40163,"errmsg":"code been used, rid: 661504e7-286e7255-0db6bc03"}
     */
    @Override
    public String getOpenid(String code) {
        //根据code缓存openid
        String cacheKey = "openid-" + code;
        //请求链接
        String reqUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appID + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code";
        Request build = new Request.Builder().url(reqUrl).build();
        try (Jedis jedis = jedisPool.getResource(); Response response = okHttpClient.newCall(build).execute()) {
            boolean exists = jedis.exists(cacheKey);
            if (exists) {
                String data = jedis.get(cacheKey);
                //存在则说明没过期且有效
                if (data != null && !data.isEmpty()) {
                    //构建响应体
                    Map<String, String> resp = new HashMap<>();
                    resp.put("openid", data);
                    return generateSucceed(resp);
                }
            }
            ResponseBody resBody = response.body();
            if (response.isSuccessful() && resBody != null) {
                String jsonString = resBody.string();
                JSONObject jsonObject = JSONObject.parseObject(jsonString);
                logger.error("获取openid:{}", jsonObject);
                String errCode = jsonObject.getString("errcode");
                String errMsg = jsonObject.getString("errmsg");
                String openid = jsonObject.getString("openid");
                Integer expiresIn = jsonObject.getInteger("expires_in");//接口超时时间
                if (errCode != null) {
                    //说明请求错误
                    return generateFail(errMsg);
                }
                //缓存openid->1小时
                jedis.setex(cacheKey, expiresIn, openid);
                //构建响应体
                Map<String, String> resp = new HashMap<>();
                resp.put("openid", openid);
                return generateSucceed(resp);
            } else {
                return generateFail("getOpenid获取resBody为null！！ ");
            }
        } catch (Exception e) {
            return generateFail("获取getOpenid异常：Exception: " + e.getMessage());
        }
    }

    /**
     * 正式环境用这个：获取StableAccessToke有效期7200秒 需全局缓存
     */
    @Override
    public String getStableAccessToken() {
        String accessTokenKey = "wx_access_token";
        //获取稳定的AccessToken
        HashMap<String, Object> params = new HashMap<>();
        params.put("grant_type", "client_credential");
        params.put("appid", appID);
        params.put("secret", appsecret);
        params.put("force_refresh", false);
        //开始请求
        String reqParams = JSON.toJSONString(params);
        RequestBody body = RequestBody.create(reqParams, MediaType.get("application/json; charset=UTF-8"));
        String reqUrl = "https://api.weixin.qq.com/cgi-bin/stable_token";
        Request request = new Request.Builder()//
                .url(reqUrl)//请求连接
                .post(body)//请求体
                .build();//构建
        //同步请求
        try (Jedis jedis = jedisPool.getResource(); Response response = okHttpClient.newCall(request).execute()) {
            boolean exists = jedis.exists(accessTokenKey);
            if (exists) {
                String s = jedis.get(accessTokenKey);
                //存在则说明没过期且有效
                if (s != null && !s.isEmpty()) {
                    return s;
                }
            }
            //不存在则说明过期了->从新获取
            ResponseBody resBody = response.body();
            if (response.isSuccessful() && resBody != null) {
                String jsonString = resBody.string();
                JSONObject jsonObject = JSONObject.parseObject(jsonString);
                logger.error("获取StableAccessToken:{}", jsonObject);
                String errCode = jsonObject.getString("errcode");
                String errMsg = jsonObject.getString("errmsg");
                String accessToken = jsonObject.getString("access_token");
                if (errCode != null) {
                    //说明请求错误
                    logger.error("获取StableAccessToken失败：{}", errCode + ":" + errMsg);
                    return "";
                }
                //设置保存时间为7200秒(5分钟内都有效)
                jedis.setex(accessTokenKey, 7200, accessToken);
                return accessToken;
            } else {
                logger.error("StableAccessToken的resBody为空");
                return "";
            }
        } catch (Exception e) {
            logger.error("getStableAccessToken失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * 根据access_token获取jsapi_ticket->先从redis中获取->jsapi_ticket有效期7200秒 需全局缓存
     */
    @Override
    public String getJsapiTicket(String accessToken) {
        if (accessToken == null || accessToken.isEmpty()) {
            return "";
        }
        //缓存key
        String jsapiTicketKey = "wx_jsapi_ticket";
        //请求链接
        String reqUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";
        Request build = new Request.Builder().url(reqUrl).build();
        try (Jedis jedis = jedisPool.getResource(); Response response = okHttpClient.newCall(build).execute()) {
            boolean exists = jedis.exists(jsapiTicketKey);
            if (exists) {
                String s = jedis.get(jsapiTicketKey);
                //存在则说明没过期且有效
                if (s != null && !s.isEmpty()) {
                    return s;
                }
            }
            //不存在则说明过期了->从新获取
            ResponseBody resBody = response.body();
            if (response.isSuccessful() && resBody != null) {
                String jsonString = resBody.string();
                JSONObject jsonObject = JSONObject.parseObject(jsonString);
                logger.error("获取JsapiTicket:{}", jsonObject);
                String errCode = jsonObject.getString("errcode");
                String errMsg = jsonObject.getString("errmsg");
                String ticket = jsonObject.getString("ticket");
                if (errCode != null && errCode.equals("0")) {
                    //说明请求成功，设置保存时间为7200秒
                    jedis.setex(jsapiTicketKey, 7200, ticket);
                    return ticket;
                } else {
                    logger.error("获取JsapiTicket错误:{}", errCode + ":" + errMsg);
                    return "";
                }
            } else {
                logger.error("获取JsapiTicket中resBody为空");
                return "";
            }
        } catch (Exception e) {
            logger.error("getJsapiTicket失败：{}", e.getMessage());
            return "";
        }
    }

    /**
     * @param url:（当前网页的URL，不包含#及其后面部分）
     * @param JsapiTicket               JsapiTicket
     */
    @Override
    public HashMap<String, String> generateSignature(String url, String JsapiTicket) {
        HashMap<String, String> data = new HashMap<>();
        if (url == null || url.isEmpty() || JsapiTicket == null || JsapiTicket.isEmpty()) {
            return data;
        }
        String nonceStr = UUID.randomUUID().toString();
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);
        String signature = "";
        //注意这里参数名必须全部小写，且必须有序
        String signature1 = "jsapi_ticket=" + JsapiTicket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
        //生成最终签名签名
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(signature1.getBytes(StandardCharsets.UTF_8));
            signature = byteToHex(crypt.digest());
        } catch (Exception e) {
            logger.error("生成签名异常：Exception: {}", e.getMessage());
        }
        data.put("nonceStr", nonceStr);
        data.put("timestamp", timestamp);
        data.put("signature", signature);
        data.put("appId", appID);
        return data;
    }


    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }
}
